using UnityEngine;
using System.Collections;

/// \class  gkVelocityController
/// \brief  Help to control the velocity
[AddComponentMenu("GK/Engine/GameCore/Movement/gkVelocityController")]
[RequireComponent (typeof (gkPhysicComponent))]
public class gkVelocityController : MonoBehaviour 
{
    /// The desired velocity
    private Vector3 m_f3DesiredVelocity;

    /// The maximum acceleration
    // If < 0 : There is no acceleration maximum
    private float m_fMaximumAccelerationAmplitude = -1.0f;

    /// The maximum acceleration override
    // If < 0 : There is no acceleration maximum
    // If == 0 : We doesn't override the maximum acceleration
    private float m_fMaximumAccelerationAmplitudeOverride;

    /// The physic component
    private gkPhysicComponent m_rPhysicComponent;

    /// \brief  Reach a velocity with a maximum acceleration 
    /// \param[in]  a_f3DesiredVelocity the desired velocity
    /// \param[in]  a_fMaximumAccelerationAmplitudeOverride the new maximum acceleration
    // If < 0 : There is no acceleration maximum
    // If == 0 : We doesn't override the maximum acceleration
    public void SetDesiredVelocity(Vector3 a_f3DesiredVelocity, float a_fMaximumAccelerationAmplitudeOverride = 0.0f)
    {
	    m_fMaximumAccelerationAmplitudeOverride = a_fMaximumAccelerationAmplitudeOverride;
	    m_f3DesiredVelocity = a_f3DesiredVelocity;
    }

    /// \brief  Reach a desired velocity in a certain time
    /// \param[in]  a_fReachVelocityDuration  The duration of the transition to the desired velocity
    ///             If < 0  : Keep the default acceleration
    ///             If == 0 : Reach the desired velocity immediately 
    public void SetDesiredVelocityInTime(Vector3 a_f3DesiredVelocity, float a_fReachVelocityDuration = 0.0f)
    {
	    float fAccelerationToReachTheVelocityInTime;
	    Vector3 f3CurrentVelocity;

	    // Get the current velocity
	    f3CurrentVelocity = m_rPhysicComponent.GetLinearVelocity();

	    if(a_fReachVelocityDuration < 0.0f)
	    {
		    // Keep the default acceleration
		    SetDesiredVelocity(a_f3DesiredVelocity);
	    }
	    else if(a_fReachVelocityDuration == 0.0f)
	    {
		    // Reach the desired velocity immediately 
		    SetDesiredVelocity(a_f3DesiredVelocity, -1.0f);
	    }
	    else
	    {
		    // Compute the required acceleration to reach the velocity in time
		    fAccelerationToReachTheVelocityInTime = (a_f3DesiredVelocity - f3CurrentVelocity).magnitude/a_fReachVelocityDuration;
		    SetDesiredVelocity(a_f3DesiredVelocity, fAccelerationToReachTheVelocityInTime);
	    }
    }

     /// \brief  Called just after the game begin to play
    void Start()
    {
        // Get the physic component
        m_rPhysicComponent = GetComponent<gkPhysicComponent>();
    }

    /// \brief  Called each frame
    void Update()
    { 	
	    // Try to reach the desired velocity
	    UpdateToReachDesiredVelocity();
    }

    /// \brief  Try to reach the desired velocity
    void UpdateToReachDesiredVelocity()
    {
	    Vector3 f3CurrentVelocity;
	    Vector3 f3NewAcceleration;
	    float fNewAccelerationAmplitude;
	    float fCurrentAccelerationAmplitudeMaximum;

	    // Compute the current acceleration maximum
	    if(m_fMaximumAccelerationAmplitudeOverride != 0.0f)
	    {
		    fCurrentAccelerationAmplitudeMaximum = m_fMaximumAccelerationAmplitudeOverride;
	    }
	    else
	    {
		    fCurrentAccelerationAmplitudeMaximum = m_fMaximumAccelerationAmplitude;
	    }

	    // Compute the needed acceleration to reach the desired velocity
	    f3CurrentVelocity = m_rPhysicComponent.GetLinearVelocity();
	    f3NewAcceleration = (m_f3DesiredVelocity - f3CurrentVelocity)/Time.deltaTime;

	    // If we must limit the acceleration amplitude
	    if(fCurrentAccelerationAmplitudeMaximum >= 0.0f)
	    {
		    fNewAccelerationAmplitude = f3NewAcceleration.magnitude;

		    // Damp the control acceleration if necessary
		    if(fNewAccelerationAmplitude > fCurrentAccelerationAmplitudeMaximum)
		    {
			    f3NewAcceleration = fCurrentAccelerationAmplitudeMaximum * (f3NewAcceleration/fNewAccelerationAmplitude);
		    }
	    }

	    // Apply the new acceleration
	    m_rPhysicComponent.SetLinearAcceleration(f3NewAcceleration);
    }
}